﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SafeScript.MetaModel.Statements
{
    public class InlineFunctionOperator : Operator
    {
        public List<Parameter> Parameters = new List<Parameter>();
        public ITypeDef ReturnType;
        public Operator Operator;
        public bool IsStatic;

        public override int GetPrecedence()
        {
            return 0;
        }

        public override ITypeDef ComputeType(ITypeDef goal, bool needsRef, ErrorReport report)
        {
            // We must infere the type. If this is impossible -> error
            if (goal == null)
            {
                report.Add(new ErrorEntry(this.Node) { Type = ErrorType.CannotInferType });
                return null;
            }
            if (goal is InstanceType)
                goal = (goal as InstanceType).Type;
            // If a binding is required, then we assume a function would be right as well.
            // This little trick here is required for type inference.
            if (goal is BindingTypeDef)
            {
                FunctionTypeDef ft = new FunctionTypeDef();
                ft.ParameterTypes = (goal as BindingTypeDef).ParameterTypes;
                ft.ReturnType = (goal as BindingTypeDef).ReturnType;
                goal = ft;
            }
            // The type to infere must be a function
            if (!(goal is FunctionTypeDef))
            {
                report.Add(new ErrorEntry(this.Node) { Type = ErrorType.CannotInferType });
                return null;
            }
            FunctionTypeDef t = goal as FunctionTypeDef;
            if ( t.ParameterTypes.Count != this.Parameters.Count)
            {
                report.Add(new ErrorEntry(this.Node) { Type = ErrorType.FunctionParameterCountMismatch });
                return null;
            }
            this.ReturnType = t.ReturnType;
            for (int i = 0; i < t.ParameterTypes.Count; ++i)
                this.Parameters[i].Type = t.ParameterTypes[i];

            ITypeDef r = this.Operator.ComputeType(this.ReturnType, false, report);
            if (r == null)
                return null;
            if (this.ReturnType == null)
                this.ReturnType = r;
            else if ( !(r.CanBeAssignedTo(this.ReturnType)))
                report.Add(new ErrorEntry(this.Node) { Type = ErrorType.ReturnTypeMismatch });
            FunctionTypeDef ftype = new FunctionTypeDef();
            ftype.ParameterTypes = t.ParameterTypes;
            ftype.ReturnType = this.ReturnType;
            return new InstanceType() { Type = ftype };
        }
    }
}
